
#include "defs.h"

#include "gurobi_c++.h"

#define GRBEXECUTE(x) \
  try{ x; }                                                             \
  catch( GRBException e ) { cerr << __FILE__ << ":" << __LINE__ << ": " << e.getErrorCode() << ": " << e.getMessage() << endl; }

void function::solveLP_BnB( linear_programming_problem_t *p_out_lp, const inference_configuration_t &c ) {

  GRBEnv   env;
  GRBModel model( env );

  /* Convert variables. */
  unordered_map<int, GRBVar> var_map;
  
  for( int i=0; i<p_out_lp->variables.size(); i++ )
    GRBEXECUTE( var_map[i] = model.addVar( 0.0, 1.0, p_out_lp->variables[i].obj_val, GRB_BINARY ) );

  model.update();
  
  /* Convert constraints. */
  for( int i=0; i<p_out_lp->constraints.size(); i++ ) {
    if( !p_out_lp->constraints[i].is_active ) continue;

    GRBLinExpr expr;
      for( int j=0; j<p_out_lp->constraints[i].vars.size(); j++ )
        expr += p_out_lp->constraints[i].coes[j] * var_map[ p_out_lp->constraints[i].vars[j] ];
    
    switch( p_out_lp->constraints[i].opr ) {
    case Equal: {
      GRBEXECUTE( model.addConstr( expr, GRB_EQUAL, p_out_lp->constraints[i].lhs ) );
      break; }
      
    case LessEqual: {
      GRBEXECUTE( model.addConstr( expr, GRB_LESS_EQUAL, p_out_lp->constraints[i].rhs ) );
      break; }

    case GreaterEqual: {
      GRBEXECUTE( model.addConstr( expr, GRB_GREATER_EQUAL, p_out_lp->constraints[i].rhs ) );
      break; }
      
//         GRBEXECUTE( m_p_model->addRange( expr, c.lhs, c.rhs ) );
      
    }
  }

  /* State a miaxmization objective (-1). */
  model.set( GRB_IntAttr_ModelSense, 1 );
  
  /* Go to hell! */
  model.getEnv().set( GRB_IntParam_OutputFlag, 0 );
  model.getEnv().set( GRB_DoubleParam_TimeLimit, c.timelimit );
  model.getEnv().set( GRB_IntParam_Threads, c.nbthreads );
  GRBEXECUTE( model.optimize() );

  /* Update the optimized value based on the solution of LP. */
  for( int i=0; i<p_out_lp->variables.size(); i++ )
    p_out_lp->variables[i].optimized = var_map[i].get(GRB_DoubleAttr_X);

  p_out_lp->optimized_obj = model.get(GRB_DoubleAttr_ObjVal);
  
}



#ifdef LS
#include "localsolver.h"

using namespace localsolver;

void function::solveLP_LS( linear_programming_problem_t *p_out_lp, const inference_configuration_t &c ) {

  LocalSolver ls;
  LSModel *p_model = ls.getModel();

  LSExpression *p_cost = p_model->createExpression( O_Sum );

  /* Convert variables. */
  unordered_map<int, LSExpression*> var_map;
  
  for( int i=0; i<p_out_lp->variables.size(); i++ ) {
    LSExpression *p_var = p_model->createExpression( O_Bool );
    var_map[i]          = p_var;
    
    if( 0.0 != p_out_lp->variables[i].obj_val )
      p_cost->addOperand( p_model->createExpression( O_Prod, p_out_lp->variables[i].obj_val, p_var ) );
  }

  /* Convert constraints. */
  for( int i=0; i<p_out_lp->constraints.size(); i++ ) {
    if( !p_out_lp->constraints[i].is_active ) continue;
    switch( p_out_lp->constraints[i].opr ) {
    case Equal: {
      LSExpression *p_sum = p_model->createExpression( O_Sum );

      for( int j=0; j<p_out_lp->constraints[i].vars.size(); j++ )
        p_sum->addOperand( p_model->createExpression( O_Prod, p_out_lp->constraints[i].coes[j], var_map[ p_out_lp->constraints[i].vars[j] ] ) );
      
      p_model->addConstraint( p_model->createExpression( O_Eq, p_sum, p_out_lp->constraints[i].lhs ) );
      break; }
      
    case LessEqual: {
      LSExpression *p_sum = p_model->createExpression( O_Sum );

      for( int j=0; j<p_out_lp->constraints[i].vars.size(); j++ )
        p_sum->addOperand( p_model->createExpression( O_Prod, p_out_lp->constraints[i].coes[j], var_map[ p_out_lp->constraints[i].vars[j] ] ) );
      
      p_model->addConstraint( p_model->createExpression( O_Leq, p_sum, p_out_lp->constraints[i].rhs ) );
      break; }
    }
  }

  /* State a maximization objective. */
  p_model->addObjective( p_cost, OD_Minimize );

  /* Go to hell! */
  p_model->close();
  LSPhase *p_phase = ls.createPhase();
  p_phase->setTimeLimit( c.timelimit );
  ls.getParam()->setNbThreads( c.nbthreads );
  ls.getParam()->setVerbosity( 0 );
  ls.solve();

  /* Update the optimized value based on the solution of LP. */
  for( int i=0; i<p_out_lp->variables.size(); i++ )
    p_out_lp->variables[i].optimized = var_map[i]->getValue();
  
}

#endif
